// HttpDownload.cpp: implementation of the CHttpDownload class.
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "HttpDownload.h"
#include "../../common/misc/MD5.h"
#include <Afxinet.h>

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

//##ModelId=450DFB69004F
CHttpDownload::CHttpDownload( HTTPDOWNLOAD& httpdown )
{
	Init( httpdown );
}

//##ModelId=450DFB690051
CHttpDownload::~CHttpDownload()
{
	
}


//##ModelId=450DFB690053
long CHttpDownload::Init( HTTPDOWNLOAD& httpdown )
{
	m_httpDownload = httpdown;

	return 0;
}

CString   Convert(CString   str,   int   sourceCodepage,   int   targetCodepage)   
{   
	int   len=str.GetLength();   

	int   unicodeLen=MultiByteToWideChar(sourceCodepage,0,str,-1,NULL,0);   

	wchar_t *   pUnicode;   
	pUnicode=new   wchar_t[unicodeLen+1];   

	memset(pUnicode,0,(unicodeLen+1)*sizeof(wchar_t));   

	MultiByteToWideChar(sourceCodepage,0,str,-1,(LPWSTR)pUnicode,unicodeLen);   

	BYTE   *   pTargetData = NULL;   
	int   targetLen=WideCharToMultiByte(targetCodepage,0,(LPWSTR)pUnicode,-1,(char   *)pTargetData,0,NULL,NULL);   

	pTargetData=new   BYTE[targetLen+1];   
	memset(pTargetData,0,targetLen+1);   

	WideCharToMultiByte(targetCodepage,0,(LPWSTR)pUnicode,-1,(char   *)pTargetData,targetLen,NULL,NULL);   

	CString   rt;   
	rt.Format("%s",pTargetData);   

	delete   pUnicode;   
	delete   pTargetData;   
	return   rt;   
} 

//##ModelId=450DFB690055
long CHttpDownload::Execute()
{
	int nRet = -1;

	m_httpDownload.nStatus = DOWN_STATUS_DOWNLOADDING;
	m_httpDownload.dwStartDownTime = GetTickCount();

	if ( m_httpDownload.sURL.Left(7).CompareNoCase("http://") != 0 )
	{
		m_httpDownload.nStatus = DOWN_STATUS_URLEMPTY_FAILED;
		m_httpDownload.dwEndDownTime = GetTickCount();
		return -1;
	}
	
	int nRetCode = 0;

	CInternetSession session(_T("HttpDownload"), 1, PRE_CONFIG_INTERNET_ACCESS);
	session.SetOption( INTERNET_OPTION_CONNECT_TIMEOUT, 5000);
	CHttpConnection* pServer = NULL;
	CHttpFile* pFile = NULL;
	
	try
	{
		CString strServerName;
		CString strObject;
		INTERNET_PORT nPort;
		DWORD dwServiceType;
		const TCHAR szHeaders[] = _T("Accept: */*\r\nAccept-Language: zh-cn\r\nUser-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322; .NET CLR 2.0.50727)\r\n");
		DWORD dwHttpRequestFlags = INTERNET_FLAG_RELOAD;

		if (!AfxParseURL(m_httpDownload.sURL, dwServiceType, strServerName, strObject, nPort) ||
			dwServiceType != INTERNET_SERVICE_HTTP)
		{
			ThrowInetFileException(DOWN_STATUS_PARSEURL_FAILED);
		}

		if ( NULL == pServer )
		{
			pServer = session.GetHttpConnection(strServerName, nPort);
		}

		pFile = pServer->OpenRequest(CHttpConnection::HTTP_VERB_GET,
			strObject, NULL, 1, NULL, NULL, dwHttpRequestFlags);
		pFile->AddRequestHeaders(szHeaders);
		pFile->SendRequest();

		DWORD dwRet;
		pFile->QueryInfoStatusCode(dwRet);

		if (dwRet != HTTP_STATUS_OK)
		{
			ThrowInetFileException(DOWN_STATUS_OPENREQUEST_FAILED);
		}

		CString strNewLocation;
		pFile->QueryInfo(HTTP_QUERY_RAW_HEADERS_CRLF, strNewLocation);

		CString sTotalLen;
		pFile->QueryInfo(HTTP_QUERY_CONTENT_LENGTH , sTotalLen);
		DWORD dwTotalLen = (DWORD)atoi(sTotalLen);

		if ( m_httpDownload.bOnlyGetlen)
		{
			if ( 0 == dwTotalLen )
				ThrowInetFileException(DOWN_STATUS_CONTENTLENGTH_FAILED);

			m_httpDownload.dwDownLen =dwTotalLen;
		}
		else if ( m_httpDownload.bSaveToFile)
		{
			if ( dwTotalLen > 1024 * 1024 * 1024 )
				ThrowInetFileException(DOWN_STATUS_CONTENTLENGTH_FAILED);

			if ( 0 == dwTotalLen )
				ThrowInetFileException(DOWN_STATUS_READBODY_FAILED);

			::DeleteFile( m_httpDownload.sTempPath);
			CFile fTemp;
			if ( !fTemp.Open( m_httpDownload.sTempPath, CFile::modeCreate | CFile::modeWrite))
				ThrowInetFileException(DOWN_STATUS_CREATELOCALFILE_FAILED);

			CMD5 fileHash, shortHash;

			m_httpDownload.dwDownLen =0;
			while ( m_httpDownload.dwDownLen < dwTotalLen)
			{
				char szBuffer[8000];
				DWORD dwLen =pFile->Read( szBuffer, sizeof(szBuffer));
				if ( 0 == dwLen)
					ThrowInetFileException(DOWN_STATUS_READBODY_FAILED);
				fTemp.Write( szBuffer, dwLen);

				fileHash.Add( szBuffer, dwLen);

				if ( m_httpDownload.dwDownLen < SHORT_HASHBUF_LEN)
				{
					if ( m_httpDownload.dwDownLen + dwLen <= SHORT_HASHBUF_LEN)
						shortHash.Add( szBuffer, dwLen);
					else
						shortHash.Add( szBuffer, SHORT_HASHBUF_LEN - m_httpDownload.dwDownLen);
				}

				m_httpDownload.dwDownLen += dwLen;

				if ( g_hMainWnd )
					::PostMessage( g_hMainWnd, WM_DOWNLOAD_PROGRESS, (WPARAM)m_httpDownload.dwDownLen, (LPARAM)dwTotalLen );
			}

			fileHash.Finish();
			shortHash.Finish();

			fTemp.Close();

			if ( m_httpDownload.dwDownLen != dwTotalLen)
				ThrowInetFileException(DOWN_STATUS_READBODY_FAILED);

			MD5 md5Hash;

			fileHash.GetHash( &md5Hash);
			m_httpDownload.sFileHash.Format("%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
				md5Hash.n[0], md5Hash.n[1], md5Hash.n[2], md5Hash.n[3],
				md5Hash.n[4], md5Hash.n[5], md5Hash.n[6], md5Hash.n[7],
				md5Hash.n[8], md5Hash.n[9], md5Hash.n[10], md5Hash.n[11],
				md5Hash.n[12], md5Hash.n[13], md5Hash.n[14], md5Hash.n[15] );

			shortHash.GetHash( &md5Hash);
			m_httpDownload.sShortFileHash.Format("%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
				md5Hash.n[0], md5Hash.n[1], md5Hash.n[2], md5Hash.n[3],
				md5Hash.n[4], md5Hash.n[5], md5Hash.n[6], md5Hash.n[7],
				md5Hash.n[8], md5Hash.n[9], md5Hash.n[10], md5Hash.n[11],
				md5Hash.n[12], md5Hash.n[13], md5Hash.n[14], md5Hash.n[15] );
		}
		else
		{
			if ( dwTotalLen > 2 * 1024 * 1024 )
			{
				ThrowInetFileException(DOWN_STATUS_CONTENTLENGTH_FAILED);
			}

			if ( 0 == dwTotalLen )
			{
				CString sBuffer;
				UINT nBodyLen = pFile->Read(sBuffer.GetBufferSetLength( 256 * 1024 + 1), 256 * 1024 );
				while (nBodyLen > 0)
				{
					m_httpDownload.sBody += sBuffer;
					m_httpDownload.dwDownLen += nBodyLen;
					nBodyLen = pFile->Read(sBuffer.GetBufferSetLength( 256 * 1024 + 1), 256 * 1024 );
				}

				m_httpDownload.sBody += sBuffer;
				m_httpDownload.dwDownLen += nBodyLen;

				if ( 0 == m_httpDownload.dwDownLen )
					ThrowInetFileException(DOWN_STATUS_READBODY_FAILED);				
			}
			else
			{
				m_httpDownload.sBody.GetBufferSetLength( dwTotalLen + 1);
				if ( pFile->Read(m_httpDownload.sBody.GetBufferSetLength( dwTotalLen ), dwTotalLen ) != dwTotalLen )
					ThrowInetFileException(DOWN_STATUS_READBODY_FAILED);

				m_httpDownload.dwDownLen =dwTotalLen;
			}

			if ( m_httpDownload.sBody.Find("charset=utf-8") != -1)
			{
				m_httpDownload.sBody = Convert(m_httpDownload.sBody,CP_UTF8,936);
				//convert to gb2312 charset
			}
		}

		m_httpDownload.nStatus = DOWN_STATUS_SUCCEED;
		m_httpDownload.dwEndDownTime = GetTickCount();
		nRet = 0;
	}
	catch (CInternetException* pEx)
	{
		pEx->Delete();
		m_httpDownload.nStatus = DOWN_STATUS_INTERNETEXCEPTION_FAILED;
		m_httpDownload.dwEndDownTime = GetTickCount();
	}
	catch (CInetFileException* pEx)
	{
		m_httpDownload.nStatus = pEx->m_nErrorCode;
		m_httpDownload.dwEndDownTime = GetTickCount();
		TRACE("Error: Exiting with CInetFileException(%d)\n", nRetCode);
		pEx->Delete();
	}
	catch (...) 
	{
		m_httpDownload.nStatus = DOWN_STATUS_OTHEREXCEPTION_FAILED;
		m_httpDownload.dwEndDownTime = GetTickCount();
		TRACE("Error: Exiting with ...(%d)\n", DOWN_STATUS_OTHEREXCEPTION_FAILED);
	}

	if (pFile != NULL) {
		pFile->Close();
		delete pFile;
		pFile = NULL;
	}

	if (pServer != NULL) {
		pServer->Close();
		delete pServer;
		pServer = NULL;
	}

	session.Close();

	return nRet;
}

//##ModelId=450DFB690056
void CHttpDownload::ThrowInetFileException(int nCode)
{	
	CInetFileException* pEx = new CInetFileException(nCode);
	throw pEx;
}
